/*******************************************************************************
 * Copyright (c) 2001-2003 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.core.resources;

import java.util.ArrayList;
import java.util.List;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.api.IPerforceServerManager;
import net.sourceforge.perforce.core.internal.P4TeamProvider;
import net.sourceforge.perforce.core.syncinfo.ResourceSyncInfo;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;

/**
  * @version $Revision: 240 $
  * @author  <a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
public class LocalFile extends LocalResource
{
	// --------------------------------------------------------------------------- Private Variables
	
	/** Stores the ResourceSyncInfo. */
	private ResourceSyncInfo syncInfo;
	
	// --------------------------------------------------------------- Public Static Factory Methods
	
	/** Call this method to create a LocalFile instance.
	  * 
	  * @param server the perforce server.
	  * @param resource the local resource.
	  * @return the LocalFile instance.
	  */
	public static final LocalFile create(IPerforceServer server, IResource resource)
	{
		return new LocalFile(server, resource);
	} 
	
	// -------------------------------------------------------------------------------- Constructors
	
	/** Constructor for LocalFile.
	  * 
	  * @see LocalResource#LocalResource(IPerforceServer, IResource)
	  */
	private LocalFile(IPerforceServer server, IResource resource)
	{
		super(server, resource);
	}

	// ----------------------------------------------------------------------- Public Static Methods

	/** Returns an array of filenames of the given files.
	  * 
	  * @param files the files.
	  * @return the filename array.
	  */
	public static String[] fileNames(LocalFile[] files)
	{
		List argumentList = new ArrayList();
		for (int i = 0, length = files.length; i < length; i++)
		{
			argumentList.add(files[i].getAbsolutePath());
		}
		
		return (String[]) argumentList.toArray(new String[argumentList.size()]);		
	}
	
	// ------------------------------------------------------------------------------ Public Methods

	/** Call this method to retrieve the name of the file.
	  * 
	  * @return the name of the file.
	  */
	public String getName()
	{
		return getLocation().lastSegment();
	}

	/** Returns the ResourceSyncInfo for the file.
	  * 
	  * @return the ResourceSyncInfo.
	  */
	public ResourceSyncInfo getSyncInfo()
	{
		if (syncInfo == null)
		{			
			try
			{
				IResource resource = toResource();
				if (resource != null && resource.exists())
				{
					syncInfo = PerforceCorePlugin.getSynchronizer().getResourceSync(resource);
					if (PerforceCorePlugin.getPlugin().getShouldRefreshWhenNotFound())
					{
						ResourceSyncInfo[] infos = 
							getServer().fstat(resource.getLocation().toOSString(), null);
						if (infos != null && infos.length == 1)
						{
							syncInfo = infos[0];
						} 
					}
				}
				else
				{
					ResourceSyncInfo[] infos = getServer().fstat(getLocation().toOSString(), null);
					if (infos != null && infos.length == 1)
					{
						syncInfo = infos[0];
					}
				}
			}
			catch (PerforceException e)
			{
				PerforceCorePlugin.log(e.getStatus());
			}
			
			if (syncInfo == null)
			{
				syncInfo = ResourceSyncInfo.NULL_INFO;
			}
		}
		
		return syncInfo;
	}

	/** Checks whether the resource has a corresponding resource object.
	  * 
	  * @return true if there is a remote object.
	  */
	public boolean hasRemote()
	{
		return !getSyncInfo().isNull();
	}
	
	/** Checks whether file is checked out locally.
	  * 
	  * @return true if file is checked out locally.
	  */
	public boolean isCheckedOut()
	{
		ResourceSyncInfo syncInfo = getSyncInfo();
		return !syncInfo.isNull() && !syncInfo.getAction().equals(""); 
	}
	
	/** @see LocalResource#isResolved() */
	public boolean isResolved() 
	{
		ResourceSyncInfo syncInfo = getSyncInfo();
		if (syncInfo == null)
		{
			return true;
		}
		
		return syncInfo.isResolved();
	}

	
	/** Checks whether the file is dirty.
	  * 
	  * @return true if file is dirty.
	  * 
	  * TODO: Check whether we can use modification date.
	  */
	public boolean isDirty()
	{
		return isCheckedOut();
	}

	/** Checks whether there is any newer remote object.
	  * 
	  * @return true if there is a more recent remote object, false otherwise.
	  */
	public boolean isOutOfDate()
	{
		ResourceSyncInfo syncInfo = getSyncInfo();
		
		boolean result = false;
		if (syncInfo != null)
		{
			result = !syncInfo.isResolved();
			result = result || (syncInfo.getHaveRev() != syncInfo.getHeadRev() && 
				!syncInfo.getHeadAction().equals(ResourceSyncInfo.ACTION_DELETE));
		}
		
		return result;
	}

	/** Reverts the file.
	  * 
	  * @throws PerforceException in case of an error.
	  */
	public void revert()
		throws PerforceException
	{
		IPerforceServerManager manager = PerforceCorePlugin.getServerManager();
		IPerforceServer server = manager.getPerforceServer(toResource());
		
		server.revert(new String[]{ getSyncInfo().getClientFileName() }, null);
		
		IResource resource = toResource();
		if (resource != null)
		{
			P4TeamProvider provider = (P4TeamProvider) RepositoryProvider.getProvider(
				resource.getProject(),
				PerforceCorePlugin.getTypeId());
				
			try
			{
				resource.refreshLocal(IResource.DEPTH_ONE, null);
				provider.refreshState(new IResource[]{ resource }, IResource.DEPTH_ONE, null);
			}
			catch (CoreException e)
			{
				throw new PerforceException(e.getStatus());
			}
			catch (TeamException e)
			{
				throw new PerforceException(e.getStatus());
			}
		}
	}	

	/** Opens the file for delete.
	  * 
	  * @throws PerforceException in case of an error.
	  */
	public void delete()
		throws PerforceException
	{
		IPerforceServerManager manager = PerforceCorePlugin.getServerManager();
		IPerforceServer server = manager.getPerforceServer(toResource());		
		
		server.delete(new String[]{ getSyncInfo().getClientFileName() });
	}	
	
	/** Syncs the file.
	  * 
	  * @param revision the revision to sync to.
	  * @param monitor for feedback.
	  * @return the status of the operation.
	  * 
	  * @throws PerforceException in case of an error.
	  */	
	public IStatus sync(String revision, IProgressMonitor monitor)
		throws PerforceException
	{
		IPerforceServerManager manager = PerforceCorePlugin.getServerManager();
		IPerforceServer server = manager.getPerforceServer(toResource());
		
		String spec = getSyncInfo().getClientFileName();
		return server.sync(spec, revision, monitor);
	}	
	
	/** Called to reopen the LocalFile in the given Changelist.
	  *
	  * @param changelist the changelist
	  * @throws PerforceException in case of an error. 
	  */
	public void reopen(IChangelist changelist)
		throws PerforceException
	{
		getServer().reopen(changelist.getId(), new String[] { getAbsolutePath() });
	}
	
	/** @see java.lang.Object#toString() */
	public String toString() 
	{
		// TODO: Check whether the depot filename is more appropriate for dialogs
		return getLocation().toOSString();
	}
	
	/** @see java.lang.Object#equals(java.lang.Object) */
	public boolean equals(Object object) 
	{
		if (object == this)
		{
			return true;
		}
		
		if (!(object instanceof LocalFile))
		{
			return false;
		}
		
		LocalFile other = (LocalFile) object;
		return getLocation().equals(other.getLocation());
	}
	
	/** @see java.lang.Object#hashCode() */
	public int hashCode() 
	{
		return getLocation().hashCode();
	}
}
